import PyInstaller.__main__
import os
import tkinter as tk
from tkinter import filedialog, messagebox
from tkinter import ttk
import sys

class PackageTool:
    def __init__(self):
        self.window = tk.Tk()
        self.window.title("Python程序打包工具")
        self.window.geometry("600x400")
        
        # 创建主框架
        self.main_frame = ttk.Frame(self.window, padding="10")
        self.main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        
        # 文件选择
        ttk.Label(self.main_frame, text="Python文件:").grid(row=0, column=0, sticky=tk.W)
        self.file_path = tk.StringVar()
        ttk.Entry(self.main_frame, textvariable=self.file_path, width=50).grid(row=0, column=1, padx=5)
        ttk.Button(self.main_frame, text="浏览", command=self.select_file).grid(row=0, column=2)
        
        # 输出名称
        ttk.Label(self.main_frame, text="输出名称:").grid(row=1, column=0, sticky=tk.W, pady=10)
        self.output_name = tk.StringVar()
        ttk.Entry(self.main_frame, textvariable=self.output_name, width=50).grid(row=1, column=1, padx=5, pady=10)
        
        # 选项框架
        option_frame = ttk.LabelFrame(self.main_frame, text="打包选项", padding="10")
        option_frame.grid(row=2, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=10)
        
        # 选项
        self.onefile = tk.BooleanVar(value=True)
        self.noconsole = tk.BooleanVar(value=False)
        self.clean = tk.BooleanVar(value=True)
        
        ttk.Checkbutton(option_frame, text="打包成单个文件", variable=self.onefile).grid(row=0, column=0, sticky=tk.W)
        ttk.Checkbutton(option_frame, text="不显示控制台", variable=self.noconsole).grid(row=0, column=1, sticky=tk.W)
        ttk.Checkbutton(option_frame, text="清理临时文件", variable=self.clean).grid(row=0, column=2, sticky=tk.W)
        
        # 图标选择
        ttk.Label(self.main_frame, text="图标文件:").grid(row=3, column=0, sticky=tk.W)
        self.icon_path = tk.StringVar()
        ttk.Entry(self.main_frame, textvariable=self.icon_path, width=50).grid(row=3, column=1, padx=5)
        ttk.Button(self.main_frame, text="浏览", command=self.select_icon).grid(row=3, column=2)
        
        # 状态显示
        self.status = tk.StringVar()
        ttk.Label(self.main_frame, textvariable=self.status).grid(row=4, column=0, columnspan=3, pady=10)
        
        # 打包按钮
        ttk.Button(self.main_frame, text="开始打包", command=self.build_exe).grid(row=5, column=0, columnspan=3, pady=10)
        
    def select_file(self):
        file = filedialog.askopenfilename(filetypes=[("Python Files", "*.py")])
        if file:
            self.file_path.set(file)
            # 自动设置输出名称
            default_name = os.path.splitext(os.path.basename(file))[0]
            self.output_name.set(default_name)
    
    def select_icon(self):
        icon = filedialog.askopenfilename(filetypes=[("Icon Files", "*.ico")])
        if icon:
            self.icon_path.set(icon)
    def build_exe(self):
        if not self.file_path.get():
            messagebox.showerror("错误", "请选择要打包的Python文件！")
            return
            
        # 检查目标文件是否被占用
        output_exe = os.path.join(os.getcwd(), 'dist', f'{self.output_name.get()}.exe')
        if os.path.exists(output_exe):
            try:
                os.chmod(output_exe, 0o777)
                os.remove(output_exe)
            except Exception as e:
                messagebox.showerror("错误", f"目标文件被占用，请关闭所有相关程序后重试：{str(e)}")
                return
    def build_exe(self):
        if not self.file_path.get():
            messagebox.showerror("错误", "请选择要打包的Python文件！")
            return
            
        # 检查目标文件是否被占用
        output_exe = os.path.join(os.getcwd(), 'dist', f'{self.output_name.get()}.exe')
        if os.path.exists(output_exe):
            try:
                os.chmod(output_exe, 0o777)
                os.remove(output_exe)
            except Exception as e:
                messagebox.showerror("错误", f"目标文件被占用，请关闭所有相关程序后重试：{str(e)}")
                return
        # 定义backup_file变量在try块之外
        backup_file = self.file_path.get() + '.bak'
        def build_exe(self):
            if not self.file_path.get():
                messagebox.showerror("错误", "请选择要打包的Python文件！")
                return
                
            # 检查目标文件是否被占用
            output_exe = os.path.join(os.getcwd(), 'dist', f'{self.output_name.get()}.exe')
            if os.path.exists(output_exe):
                try:
                    os.chmod(output_exe, 0o777)
                    os.remove(output_exe)
                except Exception as e:
                    messagebox.showerror("错误", f"目标文件被占用，请关闭所有相关程序后重试：{str(e)}")
                    return
        # 定义backup_file变量在try块之外
        backup_file = self.file_path.get() + '.bak'
        
        params = [
            self.file_path.get(),
            f'--name={self.output_name.get() or "output"}',
            '--hidden-import=requests',
            '--hidden-import=statistics',
            '--hidden-import=time',
            '--hidden-import=tkinter',  # 添加 tkinter 依赖
            '--collect-all=requests',
            '--collect-all=urllib3',
            '--collect-all=certifi',
            '--collect-all=idna',
            '--collect-all=charset_normalizer',
            '--collect-data=requests',
            '--collect-data=urllib3',
            '--collect-data=certifi',
            '--collect-data=idna',
            '--collect-data=charset_normalizer',
            f'--add-binary={os.path.join(os.path.dirname(sys.executable), "python312.dll")};.',
            f'--add-binary={os.path.join(os.path.dirname(sys.executable), "vcruntime140.dll")};.',
            '--runtime-tmpdir=.',
            # 移除 tkinter 排除
            # '--exclude-module=tkinter',
            # '--exclude-module=_tkinter',
            # '--exclude-module=Tkinter',
            # '--exclude-module=tk'
        ]
        
        try:
            self.status.set("正在打包中...")
            self.window.update()
            
            # 清理旧的构建文件
            self._clean_old_files()
            
            # 修改原始Python文件，添加暂停功能
            with open(self.file_path.get(), 'r', encoding='utf-8') as f:
                content = f.read()
            # 备份原文件
            with open(backup_file, 'w', encoding='utf-8') as f:
                f.write(content)
            # 修改源文件添加等待输入功能
            with open(self.file_path.get(), 'w', encoding='utf-8') as f:
                # 写入原始内容，但不包括 main 块
                if 'if __name__ == "__main__":' in content:
                    main_index = content.find('if __name__ == "__main__":')
                    f.write(content[:main_index])
                else:
                    f.write(content)
                
                # 添加新的 main 块
                f.write('\n\nif __name__ == "__main__":\n')
                f.write('    try:\n')
                f.write('        test_speed()\n')
                f.write('    except Exception as e:\n')
                f.write('        print(f"测试过程中出现错误: {str(e)}")\n')
                f.write('    finally:\n')
                f.write('        print("\\n按任意键退出...", end="", flush=True)\n')
                f.write('        try:\n')
                f.write('            import msvcrt\n')
                f.write('            msvcrt.getch()\n')
                f.write('        except ImportError:\n')
                f.write('            input()\n')
            
            if self.onefile.get():
                params.append('--onefile')
            if self.noconsole.get():
                params.append('--noconsole')
            if self.clean.get():
                params.append('--clean')
            if self.icon_path.get():
                params.append(f'--icon={self.icon_path.get()}')
            
            # 执行打包
            PyInstaller.__main__.run(params)
            
            # 还原原始文件
            os.replace(backup_file, self.file_path.get())
            
            # 清理临时文件
            self._clean_temp_files()
            
            self.status.set("打包完成！")
            messagebox.showinfo("成功", f"打包完成！\n文件保存在: {os.path.join(os.getcwd(), 'dist', self.output_name.get())}.exe")
        except Exception as e:
            self.status.set("打包失败！")
            messagebox.showerror("错误", f"打包过程中出现错误：{str(e)}")
        finally:
            # 清理临时文件
            if os.path.exists(backup_file):
                os.replace(backup_file, self.file_path.get())
    
    def _clean_old_files(self):
        """清理旧的构建文件"""
        dirs_to_clean = ['build', 'dist', '__pycache__']
        files_to_clean = [f'{self.output_name.get()}.spec']
        
        for dir_name in dirs_to_clean:
            if os.path.exists(dir_name):
                try:
                    import shutil
                    # 确保文件夹中的文件都可以被删除
                    for root, dirs, files in os.walk(dir_name):
                        for file in files:
                            file_path = os.path.join(root, file)
                            try:
                                os.chmod(file_path, 0o777)
                            except:
                                pass
                    shutil.rmtree(dir_name, ignore_errors=True)
                except Exception as e:
                    print(f"清理 {dir_name} 时出错: {str(e)}")
        
        for file_name in files_to_clean:
            if os.path.exists(file_name):
                try:
                    os.chmod(file_name, 0o777)
                    os.remove(file_name)
                except Exception as e:
                    print(f"删除 {file_name} 时出错: {str(e)}")
    def _clean_temp_files(self):
        """清理临时文件"""
        if os.path.exists('build'):
            import shutil
            shutil.rmtree('build')
        
        spec_file = f'{self.output_name.get()}.spec'
        if os.path.exists(spec_file):
            os.remove(spec_file)
    def run(self):
        self.window.mainloop()

if __name__ == "__main__":
    tool = PackageTool()
    tool.run()